﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DiffisionWithDecomposition
{
    public class ShannonsMutualInformation
    {
        public static double MutualInformation(int total0s, int total1s, int correct1s, int correct0s)
        {
            if (total0s < correct0s)
            {
                correct0s = total0s;
            }
            if (total1s < correct1s)
            {
                correct1s = total1s;
            }

            var false1s = total1s - correct1s;
            var false0s = total0s - correct0s;

            var P_c_0 = ((double)correct0s / total0s);
            var P_c_1 = ((double)correct1s / total1s);
            var P_e_1 = ((double)false1s / total1s);
            var P_e_0 = ((double)false0s / total0s);

            var P00 = 0.5 * P_c_0;  // 0.5 because propability of sending a 1 and sending a zero are both p=0.5 
            var P01 = 0.5 * P_e_0;
            var P10 = 0.5 * P_e_1;
            var P11 = 0.5 * P_c_1;

            var P_y_1 = P_e_0 * 0.5 + P_c_1 * 0.5;
            var P_y_0 = P_e_1 * 0.5 + P_c_0 * 0.5;

            var I00 = P00 * Math.Log(0.00001 + (P00 / (0.5 * P_y_0)), 2);
            var I01 = P01 * Math.Log(0.00001 + (P01 / (0.5 * P_y_1)), 2);
            var I10 = P10 * Math.Log(0.00001 + (P10 / (0.5 * P_y_0)), 2);
            var I11 = P11 * Math.Log(0.00001 + (P11 / (0.5 * P_y_1)), 2);

            var I = I00 + I01 + I10 + I11;

            if (double.IsNaN(I))
            {
                I = 0;
            }

            return I;
        }

        public static double MutualInformation(double pc0, double pc1)
        {            
            var P_c_0 = ((double)pc0);
            var P_c_1 = ((double)pc1);
            var P_e_1 = ((double)1 - pc1);
            var P_e_0 = ((double)1 - pc0);

            var P00 = 0.5 * P_c_0;  // 0.5 because propability of sending a 1 and sending a zero are both p=0.5 
            var P01 = 0.5 * P_e_0;
            var P10 = 0.5 * P_e_1;
            var P11 = 0.5 * P_c_1;

            var P_y_1 = P_e_0 * 0.5 + P_c_1 * 0.5;
            var P_y_0 = P_e_1 * 0.5 + P_c_0 * 0.5;

            var I00 = P00 * Math.Log(0.00001 + (P00 / (0.5 * P_y_0)), 2);
            var I01 = P01 * Math.Log(0.00001 + (P01 / (0.5 * P_y_1)), 2);
            var I10 = P10 * Math.Log(0.00001 + (P10 / (0.5 * P_y_0)), 2);
            var I11 = P11 * Math.Log(0.00001 + (P11 / (0.5 * P_y_1)), 2);

            var I = I00 + I01 + I10 + I11;

            if (double.IsNaN(I))
            {
                I = 0;
            }

            return I;
        }

        public static double MutualInformation2(double pc0, double pc1)
        {
            var P_c_0 = ((double)pc0);
            var P_c_1 = ((double)pc1);
            var P_e_1 = ((double)1 - pc1);
            var P_e_0 = ((double)1 - pc0);

            var P00 = 0.5 * P_c_0;  // 0.5 because propability of sending a 1 and sending a zero are both p=0.5 
            var P01 = 0.5 * P_e_0;
            var P10 = 0.5 * P_e_1;
            var P11 = 0.5 * P_c_1;

            var P_y_1 = P_e_0 * 0.5 + P_c_1 * 0.5;
            var P_y_0 = P_e_1 * 0.5 + P_c_0 * 0.5;

            var I00 = P00 * Math.Log(0.00001 + (P00 / (0.5 * P_y_0))) / Math.Log(2);
            var I01 = P01 * Math.Log(0.00001 + (P01 / (0.5 * P_y_1))) / Math.Log(2);
            var I10 = P10 * Math.Log(0.00001 + (P10 / (0.5 * P_y_0))) / Math.Log(2);
            var I11 = P11 * Math.Log(0.00001 + (P11 / (0.5 * P_y_1))) / Math.Log(2);

            var I = I00 + I01 + I10 + I11;

            if (double.IsNaN(I))
            {
                I = 0;
            }

            return I;
        }
    }
}
